数据库安全性管理

数据库存着一个系统最值钱的东西。这一章不讲怎么存、怎么查, 只讲一件事——怎么让该看的人看得到,不该看的人进不来。

🎯 本章学习目标
  • 说清楚数据库安全性是哪几层防线、各防线干什么
  • 读懂 MySQL 的五张权限表,知道一次登录背后到底查了哪几张表
  • 独立完成用户的增、删、改、查、改密五种操作
  • 会用 GRANT、REVOKE、SHOW GRANTS 管理权限
  • 理解角色机制,会用角色批量分配权限

8.1 数据库安全性定义

先回答一个问题:一台跑着 MySQL 的服务器被人拖走了硬盘,里面的数据会泄露吗? 答案是「会」。所以数据库安全不是一个开关,而是一层层叠起来的防线。

安全性涉及的层面

数据库安全性是个系统工程,不只是数据库管理员的事,至少涉及以下七个方面:

⚖️ 法律 · 社会 · 伦理
数据合规、隐私保护、行业规范
🏢 物理控制
机房门禁、防盗、防火、防水
📋 政策制度
谁可以申请账号、密码多久换一次
⚙️ 系统运行
日常巡检、备份恢复、应急预案
🔌 硬件控制
磁盘加密、可信硬件模块
💻 操作系统
文件权限、进程隔离、防火墙
🗄️ 数据库本身
这一章的主角
💬 人话翻译
把数据库想成银行金库。光有金库里的密码锁不够——还要有大楼的门禁、保安、监控、 甚至门口的法律告示牌。少一层,整个系统就有缺口。

安全性控制的五层方法

具体到数据库自身,安全控制有五种常用手段,从外到内层层把守:

层次手段作用
① 连接层用户登录验证「你是谁」——账号密码、IP 白名单、双因素验证
② 操作系统层文件权限数据库文件本身要靠操作系统保护,不能随便被读
③ 权限层权限管理验证「你能做什么」——能读哪些表、能不能改
④ 数据层数据加密就算拿到文件,看到的也是密文
⑤ 日志层安全审计所有操作留痕,事后可查、可溯源

用户标识与鉴定

一个人要进入数据库系统,第一关就是回答「你是谁」。系统通过「用户凭证」来确认身份:

  1. 用户名 / 用户标识符:最基本的身份凭证
  2. 用户名 + 口令(密码):目前最常用的方式
  3. 辅助信息:手机短信验证码、IP 地理位置、口令卡、生物特征
💡 安全等级
单纯用户名最低,加密码进阶,再加双因素(密码+手机验证码)最高。 现实生产环境里,root 账号通常会限定只能从特定 IP 登录,就是把第三种思路落地了。

用户存取权限控制

📖 定义
用户存取权限,是指某个用户对不同的数据对象(数据库、表、视图、字段等), 被允许执行哪些操作(查、增、改、删、建、授权等)。

权限的层次

视图:另一种限权方式

除了直接授权,还可以「为不同的人造不同的窗口」。 比如老师只看得到自己班的学生表,做法不是改权限,而是建一个只包含本班学生的视图, 把视图的查询权限给老师。这样从根上就限定了访问范围。

文件加密与安全审计

为什么还需要文件加密?

前面四层全是「在 MySQL 还活着的时候」起作用。如果有人绕过 MySQL, 直接拷走 .ibd 数据文件,前面这一切都白搭——文件本身是裸的。

⚠️ 绕过路径
服务器被拿走、备份磁带丢失、运维误操作、内部人员监守自盗——这些场景里, 攻击者根本不需要登录 MySQL 就能拿到数据。所以才需要对数据文件本身加密。

安全审计

对于敏感的关键数据(如成绩、薪资、医疗记录),要记录「谁、在什么时候、对哪条数据做了什么」。 这些记录就是审计日志。事后哪怕真出了问题,也能倒推出来。 日常分析这些日志,还能发现潜在的攻击征兆(比如某账号深夜频繁尝试登录)。

8.2 MySQL 权限系统

你输入 mysql -u root -p,按下回车的那一秒,MySQL 内部到底干了什么? 答案藏在它的两道关卡里。

权限管理机制:两道关卡

1
登录验证
核对账号、密码、来源 IP
2
权限检查
每条 SQL 都要核对权限
💬 人话翻译
第一道关卡像进园区刷工卡,回答「你是不是这里的人」; 第二道关卡像进每个房间还要刷一次门禁,回答「你能不能进这个房间做这件事」。 第一关只过一次,第二关每条 SQL 都要过。

第一关:登录验证

MySQL 收到登录请求后,去 mysql.user 表里查: 这个用户名 + 这个来源主机的组合存不存在?密码对不对?三项任何一项不匹配,登录就失败。

第二关:权限检查

登录成功之后,用户每发一条 SQL,MySQL 都要核对一次权限。 比如执行 SELECT * FROM teaching.t,MySQL 会按「全局 → 库 → 表 → 字段」的顺序查权限表, 只要某一级有权限就放行。

权限管理的五张表

权限信息分散在 mysql 数据库下的五张表里,按颗粒度从粗到细:

颗粒度 权限表 记录什么
全局(最粗) mysql.user 用户身份 + 全服务器范围的权限
数据库级 mysql.db 用户在某个数据库上的权限
表级 mysql.tables_priv 用户在某张表上的权限
字段级(最细) mysql.columns_priv 用户对某张表某个字段的权限
例程级 mysql.procs_priv 用户对存储过程/函数的权限
💡 重点理解
我们后面用的所有 CREATE USERGRANTREVOKE 命令, 本质上都是在改这五张表里的记录。不同的命令,影响不同的表。这一点搞清楚了, 后面所有命令的「为什么这样写」就都明白了。

mysql.user 表的关键字段

⚠️ 注意
User + Host 一起才是主键。这意味着 'teacher1'@'localhost''teacher1'@'%' 是两个不同的用户——同一个用户名从不同地方登录,是分开管的。
即学即练

用户 'admin'@'192.168.1.5' 登录后想查询 teaching.t 表, MySQL 会按什么顺序查权限表?

A. 只查 mysql.user 一张表
B. 先查 mysql.tables_priv,没有再查 mysql.user
C. 全局(user)→ 库(db)→ 表(tables_priv)→ 字段(columns_priv),任一级有权限就放行
D. 只查 mysql.tables_priv 一张表
MySQL 的权限检查是从粗到细累加的。如果在 mysql.user 里就有 Select_priv = Y(全局查询权限), 那查任何库任何表都不用再问。如果全局没有,就再看库级、表级、字段级。 任一级匹配上就放行——所以选 C。

8.3 MySQL 用户管理

用户管理就五件事:建、查、改名、改密、删。下面每一个都给完整命令,复制就能跑。

⚠️ 操作前提
下面所有命令都需要用 root 账号或具有 CREATE USER 权限的账号 登录后执行。 连接命令:mysql -u root -p,回车后输入 root 密码进入交互界面。

① 添加用户:CREATE USER

完整语法

SQL
CREATE USER [IF NOT EXISTS] '用户名'[@'主机地址或标识']
    [IDENTIFIED [WITH auth_plugin] BY '密码' | RANDOM PASSWORD]
    [WITH resource_option ...]
    [password_option];
💬 各部分解释
  • IF NOT EXISTS:如果用户已存在就跳过,避免报错
  • '用户名'@'主机':必须用单引号;% 表示任意主机,localhost 表示本机
  • IDENTIFIED BY '密码':设置明文密码(MySQL 自动加密存储)
  • RANDOM PASSWORD:让 MySQL 随机生成密码并打印出来
  • WITH MAX_QUERIES_PER_HOUR n:每小时查询次数上限

例 1:创建一个最简单的本地用户

SQL
-- 创建用户 student,密码 student123,只允许从本机登录
CREATE USER IF NOT EXISTS 'student'@'localhost'
    IDENTIFIED BY 'student123';

例 2:一次创建多个用户

SQL
-- 同时创建两个用户:
-- teacher1 允许从任何主机连过来(% 表示任意IP)
-- teacher2 只允许从指定IP登录,密码由系统随机生成
CREATE USER 'teacher1'@'%' IDENTIFIED BY 'teacher123',
            'teacher2'@'192.168.1.23' IDENTIFIED BY RANDOM PASSWORD;

例 3:带资源限制的用户

SQL
-- 创建 teacher5,主机不限
-- 限制:每小时最多查询 60 次、更新 100 次
CREATE USER 'teacher5'@'%'
    IDENTIFIED BY 'teacher5pwd'
    WITH MAX_QUERIES_PER_HOUR 60
         MAX_UPDATES_PER_HOUR 100;
❌ 易错点
  • 用户名和主机的引号是单引号,不是反引号也不是双引号
  • 不写 @'主机' 时,MySQL 默认 @'%'(允许所有主机)。生产环境建议显式指定,避免账号被外网爆破
  • 不要把密码写成明文存进表里——直接用 IDENTIFIED BY,MySQL 会自动加密

① 补充:直接 INSERT 系统表(了解即可)

因为 CREATE USER 本质就是往 mysql.user 表插记录,理论上可以直接 INSERT:

SQL · 不推荐
-- 直接往 mysql.user 表插入记录创建用户
-- 字段顺序:host, user, 密码哈希, ssl 类型, ssl 密钥, x509 颁发者, x509 主体
INSERT INTO mysql.user
    (host, user, authentication_string,
     ssl_type, ssl_cipher, x509_issuer, x509_subject)
VALUES('localhost', 'teacher6', SHA('t6123'), '', '', '', '');

-- 必须刷新权限缓存,否则新用户无法登录
FLUSH PRIVILEGES;
❌ 强烈不推荐这么做
  1. 容易写错字段、写错值,搞坏权限表
  2. 必须手动 FLUSH PRIVILEGES 才能生效
  3. 新版 MySQL 的密码加密算法不只是 SHA,自己拼可能不兼容
生产环境 永远用 CREATE USER。这里讲只是为了让你理解原理。

② 查看用户

列出所有用户

SQL
-- 查询 mysql.user 表的用户名和主机信息
SELECT User, Host FROM mysql.user;

查看某个用户的详细配置

SQL
-- 查看 teacher1 的用户名、主机、每小时最大查询次数
SELECT User, Host, max_questions
FROM mysql.user
WHERE User = 'teacher1';
运行结果
UserHostmax_questions
teacher1%0

max_questions = 0 表示无限制(默认值)。

查看当前登录的是谁

SQL
-- 这两个函数在调试权限问题时非常有用
SELECT CURRENT_USER(), USER();

③ 修改用户口令

MySQL 提供了多种改密码的方式,推荐用 ALTER USER,其他几种了解即可。

方式 1:ALTER USER(推荐)

SQL · 推荐
-- 把 student@localhost 的密码改成 student123
ALTER USER 'student'@'localhost' IDENTIFIED BY 'student123';

方式 2:SET PASSWORD

SQL
-- 给指定用户改密码
SET PASSWORD FOR 'student'@'localhost' = 'student123';

-- 不写 FOR 子句则修改当前登录用户自己的密码
SET PASSWORD = 'newpwd456';

方式 3:mysqladmin 命令(在系统 shell 里执行,不是在 mysql 里)

SHELL
# 注意:这是 Windows/Linux 命令行命令,不是 SQL 语句
# 执行后会先要求输入旧密码,再要求输入新密码
mysqladmin -u student -p password

方式 4:UPDATE 系统表(不推荐)

SQL · 不推荐
UPDATE mysql.user
SET authentication_string = SHA('student123')
WHERE User = 'student' AND Host = 'localhost';

-- 直接改表必须刷新权限
FLUSH PRIVILEGES;
⚠️ 不同方式的对比
四种方式都能改密码,但只有 ALTER USER 是 MySQL 8 推荐的标准做法。 它会自动用当前的加密插件,不需要 FLUSH PRIVILEGES,也不会因为加密算法升级而失效。

④ 重命名用户:RENAME USER

SQL
-- 完整语法:可同时改用户名和主机信息
RENAME USER '原用户名'@'原主机' TO '新用户名'@'新主机';
SQL
-- 例:把 teacher1 重命名为 teacher10
-- 同时把允许登录的主机从 % (任意) 改为 localhost (仅本机)
RENAME USER 'teacher1'@'%' TO 'teacher10'@'localhost';
⚠️ 注意事项
  • 需要 mysql 数据库的 UPDATE 权限,或服务器级 CREATE USER 权限
  • 如果该用户被设置为某个视图、存储过程的 DEFINER,MySQL 会拒绝重命名
  • 原用户的所有权限会随用户名一起迁移,不需要重新授权

⑤ 删除用户:DROP USER

SQL
-- 删除单个用户
DROP USER 'student'@'localhost';

-- 一次删除多个用户
DROP USER 'teacher5'@'%', 'teacher6'@'localhost';

-- 用户不存在时不报错
DROP USER IF EXISTS 'student'@'localhost';
❌ 危险操作
DROP USER 会同时删除该用户的所有权限。删错了不可恢复, 生产环境务必先确认 SHOW GRANTS FOR 看清这个用户的权限范围再操作。
即学即练

下列哪个语句能创建一个用户名为 guest、密码为 g123, 且只允许从本机登录的用户?

A. CREATE USER 'guest' PASSWORD 'g123';
B. CREATE USER 'guest'@'localhost' IDENTIFIED BY 'g123';
C. CREATE USER 'guest'@'%' IDENTIFIED BY 'g123';
D. NEW USER 'guest'@'localhost' WITH 'g123';
A 写法不对,没有 PASSWORD 关键字这种语法。 B 正确:用 @'localhost' 限定来源主机,IDENTIFIED BY 设密码。 C 中 % 表示任意主机,与「只允许从本机登录」不符。 D 没有 NEW USER 这种语法。

8.4 MySQL 权限管理

建好用户只是开始。新建的用户除了能登录,几乎啥都干不了——它需要被「授权」。 权限管理三件事:授(GRANT)、看(SHOW GRANTS)、收(REVOKE)。

权限的四个层次

授权时先要想清楚:你要给的是「哪一级」的权限?

级别ON 子句写法影响的表
全局(服务器级)*.*mysql.user
数据库级db_name.*mysql.db
表级db_name.table_namemysql.tables_priv
字段级db_name.table_name + 列名(在权限后括号里)mysql.columns_priv
例程级db_name.routine_namemysql.procs_priv

① 授予权限:GRANT

完整语法

SQL
GRANT 权限名称 [(字段列表)] [, 权限名称 [(字段列表)]] ...
    ON 授权层次及对象
    TO '用户名'@'主机' [, '用户名'@'主机'] ...
    [WITH GRANT OPTION];
💬 三个子句各干什么
  • 权限名称:SELECT、INSERT、UPDATE、DELETE、ALL 等等
  • ON 子句:作用对象,按级别有 *.* / db.* / db.tbl 几种写法
  • TO 子句:授给谁,可以同时给多个用户
  • WITH GRANT OPTION:被授权的人能不能再把这个权限转授给别人

例 1:授予全局所有权限(最大)

SQL
-- 给 teacher10 服务器上的所有权限,并允许它再把权限转授给别人
GRANT ALL
    ON *.*
    TO 'teacher10'@'localhost'
    WITH GRANT OPTION;

例 2:授予全局的部分权限

SQL
-- 给 teacher6 服务器级别的查询、插入、更新权限(不允许转授)
GRANT SELECT, INSERT, UPDATE
    ON *.*
    TO 'teacher6'@'localhost';

例 3:授予数据库级权限

SQL
-- 给 teacher4 在 teaching 库上的查询、建表、插入权限
-- ON teaching.* 表示 teaching 库下的所有表/视图
GRANT SELECT, CREATE, INSERT
    ON teaching.*
    TO 'teacher4'@'localhost';

例 4:授予表级权限

SQL
-- 给 teacher3 在 teaching.t 表上的增删改查权限
GRANT SELECT, INSERT, UPDATE, DELETE
    ON teaching.t
    TO 'teacher3'@'localhost';

例 5:授予字段级权限(最细)

SQL
-- 只允许 teacher2 查询 t 表的 tno 和 tn 两个字段
-- 字段名写在权限名后面的括号里
GRANT SELECT(tno, tn)
    ON teaching.t
    TO 'teacher2'@'192.168.1.23';
⚠️ 字段级权限的限制
字段级权限只支持三种操作:SELECTINSERTUPDATE。 其他权限(如 DELETE)必须授给整张表,因为「只删除某些列」在 SQL 里是无意义的。
❌ MySQL 8 的重要变化
MySQL 8 不允许给不存在的用户授权。 早期版本的 GRANT 可以同时创建用户,现在必须先 CREATE USER,再 GRANT

② 查看权限:SHOW GRANTS

SQL
-- 查看指定用户的所有权限
SHOW GRANTS FOR 'student'@'localhost';

-- 查看当前登录用户自己的权限
SHOW GRANTS FOR CURRENT_USER;

-- 上面这条还可以简写为
SHOW GRANTS;
运行结果(示意)
+--------------------------------------------------------+
| Grants for student@localhost                           |
+--------------------------------------------------------+
| GRANT USAGE ON *.* TO `student`@`localhost`            |
| GRANT SELECT ON `teaching`.* TO `student`@`localhost`  |
+--------------------------------------------------------+
💡 USAGE 是什么
GRANT USAGE ON *.* 永远会出现,它表示「这个用户存在,可以登录,但没有任何实质权限」。 这是 MySQL 用来记录「用户存在」的占位行,不用专门给。

③ 回收权限:REVOKE

语法和 GRANT 几乎对称,只是 TO 换成 FROM,没有 WITH GRANT OPTION

SQL
REVOKE 权限名称 [(字段列表)] ...
    ON 回收权限的层次及对象
    FROM '用户名'@'主机';

例:回收 SELECT 和 DELETE 权限

SQL
-- 收回 teacher10 在 teaching 库上的 SELECT 和 DELETE 权限
REVOKE SELECT, DELETE
    ON teaching.*
    FROM 'teacher10'@'localhost';

一次回收所有权限

SQL
-- 一键收回该用户的所有权限和转授权(用户本身还在)
REVOKE ALL PRIVILEGES, GRANT OPTION
    FROM 'teacher10'@'localhost';

④ 权限转移:WITH GRANT OPTION

授权的时候在末尾加上 WITH GRANT OPTION, 被授权的用户就有了「再授权给别人」的能力。这就是权限转移

SQL
-- 给 teacher3 在 teaching.t 上的 SELECT 和 UPDATE 权限
-- 同时允许 teacher3 把这两个权限再授给别的用户
GRANT SELECT, UPDATE
    ON teaching.t
    TO 'teacher3'@'localhost'
    WITH GRANT OPTION;
⚠️ 慎用 WITH GRANT OPTION
一旦给了某用户转授权,它就能把权限给其他人,权限会像树一样向下扩散。 一般只对「需要管理一群下级用户」的中层管理员账号才给。普通业务账号绝对不要给。

8.5 MySQL 角色管理

想象一所学校有 200 个老师,每个都要查教师表、查成绩表、改自己班的成绩。 难道要把同样的几条 GRANT 重复执行 200 遍?角色就是为了解决这个痛点。

角色是什么

📖 定义
角色(Role)是一组权限的集合。可以把它想成一顶帽子——帽子里装着一堆权限。 把帽子戴在用户头上,用户就有了帽子里的所有权限。
💬 没有角色的世界 vs 有角色的世界
没有角色:给 200 个老师每个人挨个执行 5 条 GRANT,一共 1000 条命令。 要改权限的时候再来 1000 条 REVOKE + GRANT。
有角色:定义一个 teacher 角色,给它 5 条 GRANT。然后把这个角色「戴」给 200 个老师。 要改权限只改角色就行,所有老师跟着变。

MySQL 8 开始支持角色机制。一个用户可以同时被分配多个角色,一个角色也可以分给多个用户。

角色的生命周期

从创建到撤销,一个角色经历五个阶段:

1
创建
CREATE ROLE
2
授权
GRANT 权限 TO 角色
3
分配
GRANT 角色 TO 用户
4
激活
SET DEFAULT ROLE
5
撤销
REVOKE / DROP ROLE
⚠️ 第 4 步「激活」最容易被忽略
把角色分配给用户后,角色默认是「未激活」状态,权限不生效! 必须执行 SET DEFAULT ROLE 让角色生效。这是初学者最常踩的坑。

第 1 步:创建角色

SQL
-- 语法(角色和用户长得很像,都是 名字@主机 格式)
CREATE ROLE '角色名'[@'主机'] [, '角色名'[@'主机']] ...;
SQL
-- 例 1:一次创建两个全局可用的角色
CREATE ROLE 'teacher', 'administrator';

-- 例 2:创建只能在本机生效的角色
CREATE ROLE 'developer'@'localhost';

第 2 步:给角色授权

给角色授权的语法和给用户授权一模一样——把 TO 后面的用户名换成角色名即可。

SQL
-- 例 1:管理员角色,授予所有权限
GRANT ALL PRIVILEGES
    ON *.*
    TO 'administrator';

-- 例 2:教师角色,授予 teaching.sc 表的增删改查
GRANT SELECT, INSERT, UPDATE, DELETE
    ON teaching.sc
    TO 'teacher';
❌ 容易拼错的关键字
PRIVILEGES,不是 PREVILEGE 也不是 PREVILEGES。 PPT 上有个手误,注意别照着写。

第 3 步:把角色分配给用户

SQL
-- 语法:GRANT 角色 TO 用户
GRANT '角色名' TO '用户名'@'主机';
SQL
-- 把 teacher 角色分配给 teacher2 用户
GRANT 'teacher' TO 'teacher2'@'%';
⚠️ 此刻 teacher2 还没获得权限
分配 ≠ 生效。这一步只是建立了「绑定关系」,下一步必须激活才能真正用上。

第 4 步:激活角色

SQL
-- 语法:让指定用户的指定角色在登录时自动激活
SET DEFAULT ROLE '角色名' TO '用户名'@'主机';
SQL
-- 例 1:让 teacher2 的 teacher 角色生效
SET DEFAULT ROLE 'teacher' TO 'teacher2'@'%';

-- 例 2:让 teacher3 拥有的所有角色都生效
SET DEFAULT ROLE ALL TO 'teacher3'@'%';

查看当前生效的角色

SQL
-- 用户登录后查询当前生效的角色
SELECT CURRENT_ROLE();
运行结果
CURRENT_ROLE()
`teacher`@`%`

第 5 步:撤销与删除

从某个用户身上回收角色

SQL
-- 语法:REVOKE 角色 FROM 用户
-- 注意被收的是「角色」,从「用户」身上拿走
REVOKE '角色名' FROM '用户名'@'主机';

-- 例:把 teacher 角色从 teacher2 身上拿走
REVOKE 'teacher' FROM 'teacher2'@'%';
❌ 顺序千万别弄反
回收角色的语法是「把角色 FROM 用户」,不是「把用户 FROM 角色」。
错误写法:REVOKE 'teacher2' FROM 'teacher';(这会被解释为「从 teacher 角色上回收 teacher2」,逻辑错乱)
正确写法:REVOKE 'teacher' FROM 'teacher2'@'%';

彻底删除一个角色

SQL
-- 把整个角色从系统里删掉,所有绑定关系自动解除
DROP ROLE 'teacher';
即学即练

下列关于 MySQL 角色的说法,错误的是?

A. 一个用户可以同时被分配多个角色
B. 一个角色也可以分给多个用户
C. 用 GRANT 把角色赋给用户后,用户立即就有了角色的所有权限
D. DROP ROLE 会自动解除该角色与所有用户的绑定关系
C 是错的——这是初学者最常踩的坑。GRANT 角色给用户只是建立绑定关系, 必须再执行 SET DEFAULT ROLE 让角色激活,权限才真正生效。否则用户登进去会发现「明明给我赋角色了,怎么没权限」。

本章小结

📋 一节课记住这五件事
  1. 数据库安全有五层防线:登录、文件、权限、加密、审计。本章主攻「权限」这一层。
  2. MySQL 用五张表存权限:user、db、tables_priv、columns_priv、procs_priv,从粗到细。所有 GRANT/REVOKE 本质都是改这五张表。
  3. 用户管理用 CREATE USER / ALTER USER / DROP USER 这三件套,记住「用户名@主机」是联合主键。
  4. 权限管理用 GRANT … ON … TO … 三段式,WITH GRANT OPTION 决定能不能转授。回收用 REVOKE … ON … FROM …,唯一区别就是 TO 换成 FROM。
  5. 角色是权限的集合,五步走:CREATE ROLE → GRANT 权限给角色 → GRANT 角色给用户 → SET DEFAULT ROLE 激活 → REVOKE / DROP。第四步最容易忘。

综合练习

第 1 题 · 模仿

哪条语句能创建一个用户名 dev、密码 dev2025,可以从任意主机登录的用户?

A. CREATE USER dev IDENTIFIED BY dev2025;
B. CREATE USER 'dev'@'%' IDENTIFIED BY 'dev2025';
C. CREATE USER 'dev'@'localhost' IDENTIFIED BY 'dev2025';
D. ADD USER 'dev'@'%' WITH 'dev2025';
用户名和密码都要单引号,主机用 % 表示任意。C 写成 localhost 就只能本机登录。
第 2 题 · 理解

执行 GRANT SELECT(name, age) ON school.student TO 'reader'@'%'; 后, reader 用户能做什么?

A. 查询 student 表的所有字段
B. 只能查询 student 表的 name 和 age 两个字段
C. 能查询 school 库所有表的 name 和 age 字段
D. 能修改 student 表的 name 和 age 字段
括号里的 (name, age) 限定了字段范围,所以这是「字段级 SELECT 权限」, 只能查 name 和 age 两列。
第 3 题 · 辨析

下列哪一组操作可以让 'mary'@'localhost' 真正拥有 analyst 角色定义的所有权限?

A. 只执行 CREATE ROLE 'analyst';
B. 只执行 GRANT 'analyst' TO 'mary'@'localhost';
C. 先 GRANT 权限 TO 'analyst',再 GRANT 'analyst' TO 'mary'@'localhost',最后 SET DEFAULT ROLE 'analyst' TO 'mary'@'localhost'
D. 只执行 SET DEFAULT ROLE 'analyst' TO 'mary'@'localhost'
完整的角色生效要走三步:① 给角色装权限 ② 把角色绑给用户 ③ 激活。 A 没装权限也没绑用户。B 绑了但没激活,权限不生效。D 没绑就没法激活。只有 C 完整。
第 4 题 · 应用

已知 'tom'@'%' 用户已存在。要让 tom 在 sales 数据库的所有表上有查询权限, 并且 能把查询权限转授给别人,正确的语句是?

A. GRANT SELECT ON sales.* TO 'tom'@'%';
B. GRANT SELECT ON sales.* TO 'tom'@'%' WITH GRANT OPTION;
C. GRANT SELECT ON sales TO 'tom'@'%' WITH GRANT OPTION;
D. GRANT ALL ON sales.* FROM 'tom'@'%' WITH GRANT OPTION;
A 没加 WITH GRANT OPTION,不能转授。
C 写成 sales 而不是 sales.*,会被解释为表名而非数据库。
D 用了 FROM,那是回收语法。
B 正确。
第 5 题 · 挑战

管理员执行了 REVOKE INSERT ON db1.* FROM 'jerry'@'%';, 但 jerry 仍然可以往 db1 中的表插入数据。最可能的原因是?

A. REVOKE 命令没有生效,需要 FLUSH PRIVILEGES
B. jerry 在更高层级(如 *.*)还有 INSERT 权限,或通过角色获得了该权限
C. REVOKE 在 MySQL 中需要先关闭数据库才生效
D. REVOKE 不能取消 INSERT 权限
权限是分层授予的,回收某一层不会自动收回更高层的同名权限。 如果 jerry 还在全局(*.*)有 INSERT,或被授予了某个含 INSERT 的角色, 那库级 REVOKE 就「白回收了」。排查时一定要 SHOW GRANTS FOR 'jerry'@'%' 把所有授权看全。